#include "orangepch.h"
#include "Application.h"
#include "Log.h"
#include "Input.h"
#include "GLFW/glfw3.h"
#include "Renderer/Renderer.h"

#define BIND_ON_EVENT(x) std::bind(&Application::x , this , std::placeholders::_1)

namespace Orange {
	
	Application* Application::m_instance = nullptr;

	Application::Application(const std::string& name ) {

		ORANGE_CORE_ASSERT((!m_instance),"Application is Created!")

		m_instance = this;

		m_Window = Scope<Window>(Window::Create(WindowProps(name)));
		m_Window->SetEventCallback(BIND_ON_EVENT(OnEvent));
		
		Renderer::Init();

		m_ImGuiLayer = new ImGuiLayer();

		PushOverLayer(m_ImGuiLayer);

		
	
		
	}

	Application::~Application() {

	}


	void Application::PushLayer(Layer* layer) {
		
		m_LayerStack.PushLayer(layer);

		layer->OnAttach();
	}
	void Application::PushOverLayer(Layer* layer) {
		m_LayerStack.PushOverLayer(layer);
		layer->OnAttach();
	}
	void Application::OnEvent(Event& e) {

		EventDispatcher dispatcher(e);
		
	
		dispatcher.Dispatch<WindowCloseEvent>(BIND_ON_EVENT(OnWindowClose));
		dispatcher.Dispatch<WindowResizeEvent>(BIND_ON_EVENT(OnWindowResize));

	
			for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();)
			{
				
				
				(*--it)->OnEvent(e);
				if (e.m_Handle) {
					break;
				}
			}

	}

	

	void Application::Run() {
		
		while (m_Running) {
		
			float now = (float)glfwGetTime();

			Time detalTime = now - m_lastFrameTime;

			m_lastFrameTime = now;
			
			if (!m_Minied) {
				for (Layer* layer : m_LayerStack) {
					layer->OnUpdate(detalTime);
				}
			}
			
			m_ImGuiLayer->Begin();

			for (Layer* layer : m_LayerStack) {
				layer->OnImGuiRender();
			}

			m_ImGuiLayer->End();

			m_Window->OnUpdate();
		};
	}

	bool Application::OnWindowClose(WindowCloseEvent& e) {
		m_Running = false;
		return true;
	}
	bool Application::OnWindowResize(WindowResizeEvent& e) {
		if (e.GetWidth() == 0 || e.GetHeight() == 0) {
			m_Minied = true;
			return false;
		}
		m_Minied = false;

		Renderer::OnWindowResize(e.GetWidth(), e.GetHeight());

		return false;
	}
}
